package com.example.demo.web.handler;

import cn.dev33.satoken.exception.NotLoginException;
import cn.hutool.core.util.StrUtil;
import com.example.demo.domain.base.BaseResult;
import com.example.demo.exception.BizException;
import com.example.demo.exception.ErrorConstants;
import com.example.demo.util.TraceUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.scheduling.annotation.Async;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 全局异常处理器：统一处理所有Controller层抛出的异常
 * 遵循RESTful规范，根据异常类型返回对应HTTP状态码和标准化响应体
 *
 * @author haochen78
 */
@Slf4j
@RestControllerAdvice
public class GlobalApiExceptionHandler {

    // 常量定义：参数校验相关提示
    private static final String NOT_NULL_MSG = "不能为null";
    private static final String NOT_EMPTY_MSG = "不能为空";

    // -------------------------- 业务异常处理 --------------------------

    /**
     * 处理自定义业务异常（BizException）
     * 从异常中提取业务错误码、消息和HTTP状态码，动态返回
     */
    @ExceptionHandler(BizException.class)
    public ResponseEntity<BaseResult<?>> handleBizException(BizException ex) {
        log.warn("[业务异常] traceId: {} | 业务码: {} | 消息: {}", TraceUtil.getTraceId(), ex.getCode(), ex.getMessage());
        BaseResult<?> result = BaseResult.failure(ex.getCode(), ex.getMessage());
        return ResponseEntity.status(ex.getStatus()).body(result);
    }

    // -------------------------- 参数校验异常处理 --------------------------

    @ExceptionHandler(HttpMessageNotReadableException.class)
    public ResponseEntity<BaseResult<?>> handleHttpMessageNotReadableException(HttpMessageNotReadableException ex) {
        log.warn("[请求体格式错误，请检查JSON是否合法] traceId: {}  \n异常详情: {}", TraceUtil.getTraceId(), ex.getMessage());
        ErrorConstants error = ErrorConstants.CLIENT_BODY_FORMAT_ERROR;
        BaseResult<?> result = BaseResult.failure(error.getBizCode(), error.getMsgTemplate());
        return ResponseEntity.status(error.getHttpStatus()).body(result);
    }

    /**
     * 处理@RequestBody参数校验失败（MethodArgumentNotValidException）
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<BaseResult<?>> handleMethodArgumentNotValid(MethodArgumentNotValidException ex) {
        log.warn("[请求体参数校验异常] traceId: {}  \n异常详情: {}", TraceUtil.getTraceId(), ex.getMessage());
        BindingResult bindingResult = ex.getBindingResult();
        BaseResult<?> result = buildParamErrorResult(bindingResult);
        return ResponseEntity.status(ErrorConstants.CLIENT_DEFAULT_PARAM_ERROR.getHttpStatus()).body(result);
    }

    /**
     * 处理@RequestParam/@PathVariable参数校验失败（ConstraintViolationException）
     */
    @ExceptionHandler(ConstraintViolationException.class)
    public ResponseEntity<BaseResult<?>> handleConstraintViolation(ConstraintViolationException ex) {
        List<String> errorMessages = ex.getConstraintViolations().stream().map(ConstraintViolation::getMessage)
                .collect(Collectors.toList());
        String errorMsg = CollectionUtils.isNotEmpty(errorMessages) ? errorMessages.get(0) : "";
        log.warn("[参数校验异常] traceId: {}  \n异常详情: {}", TraceUtil.getTraceId(), StrUtil.join("; ", errorMessages));
        BaseResult<?> result = BaseResult.failure(ErrorConstants.CLIENT_DEFAULT_PARAM_ERROR.getBizCode(), errorMsg);
        return ResponseEntity.status(ErrorConstants.CLIENT_DEFAULT_PARAM_ERROR.getHttpStatus()).body(result);
    }

    /**
     * 处理请求参数缺失异常（MissingServletRequestParameterException）
     */
    @ExceptionHandler(MissingServletRequestParameterException.class)
    public ResponseEntity<BaseResult<?>> handleMissingParam(MissingServletRequestParameterException ex) {
        String paramName = ex.getParameterName();
        log.warn("[参数缺失异常] traceId: {} | 缺失参数: {}", TraceUtil.getTraceId(), paramName);
        ErrorConstants error = ErrorConstants.CLIENT_LESS_PARAM_ERROR.format(paramName);
        BaseResult<?> result = BaseResult.failure(error.getBizCode(), error.getMsgTemplate());
        return ResponseEntity.status(error.getHttpStatus()).body(result);
    }

    /**
     * 处理参数类型不匹配异常（如String转Long失败）
     */
    @ExceptionHandler(MethodArgumentTypeMismatchException.class)
    public ResponseEntity<BaseResult<?>> handleTypeMismatch(MethodArgumentTypeMismatchException ex) {
        String paramName = ex.getName();
        String expectedType = ex.getRequiredType() != null ? ex.getRequiredType().getSimpleName() : "未知类型";
        String errorMsg = StrUtil.format("参数{}类型错误，期望{}", paramName, expectedType);
        log.warn("[参数类型异常] traceId: {}  \n异常详情: {}", TraceUtil.getTraceId(), errorMsg);
        ErrorConstants error = ErrorConstants.CLIENT_PARAM_FORMAT_ERROR.format(paramName, expectedType);
        BaseResult<?> result = BaseResult.failure(error.getBizCode(), error.getMsgTemplate());
        return ResponseEntity.status(error.getHttpStatus()).body(result);
    }

    /**
     * 处理表单参数绑定异常（BindException）
     */
    @ExceptionHandler(BindException.class)
    public ResponseEntity<BaseResult<?>> handleBindException(BindException ex) {
        log.warn("[表单参数绑定异常] traceId: {}  \n异常详情: {}", TraceUtil.getTraceId(), ex.getMessage());
        BindingResult bindingResult = ex.getBindingResult();
        BaseResult<?> result = buildParamErrorResult(bindingResult);
        return ResponseEntity.status(ErrorConstants.CLIENT_DEFAULT_PARAM_ERROR.getHttpStatus()).body(result);
    }

    // -------------------------- 系统异常处理 --------------------------

    /**
     * 未登录
     */
    @ExceptionHandler(NotLoginException.class)
    public ResponseEntity<BaseResult<?>> handleNotLoginException(NotLoginException ex) {
        log.error("[未登录异常] traceId: {} \n异常详情", TraceUtil.getTraceId(), ex);
        // 使用预定义的处理失败错误码
        ErrorConstants error = ErrorConstants.CLIENT_TOKEN_INVALID;
        BaseResult<?> result = BaseResult.failure(error.getBizCode(),
                String.format(error.getMsgTemplate(), ex.getMessage()));
        return ResponseEntity.status(error.getHttpStatus()).body(result);
    }

    ///**
    // * 权限不足
    // */
    //@ExceptionHandler(NotPermissionException.class)
    //public ResponseEntity<BaseResult<?>> handleNotPermissionException(NotPermissionException ex) {
    //    log.error("[权限不足异常] traceId: {} \n异常详情", TraceUtil.getTraceId(), ex);
    //    // 使用预定义的处理失败错误码
    //    ErrorConstants error = ErrorConstants.CLIENT_PERMISSION_DENIED;
    //    BaseResult<?> result = BaseResult.failure(error.getBizCode(),
    //            String.format(error.getMsgTemplate(), ex.getMessage()));
    //    return ResponseEntity.status(error.getHttpStatus()).body(result);
    //}
    //
    ///**
    // * 角色不符
    // */
    //@ExceptionHandler(NotRoleException.class)
    //public ResponseEntity<BaseResult<?>> handleNotRoleException(NotRoleException ex) {
    //    log.error("[角色不符异常] traceId: {} \n异常详情", TraceUtil.getTraceId(), ex);
    //    // 使用预定义的处理失败错误码
    //    ErrorConstants error = ErrorConstants.CLIENT_ROLE_MISMATCH;
    //    BaseResult<?> result = BaseResult.failure(error.getBizCode(),
    //            String.format(error.getMsgTemplate(), ex.getMessage()));
    //    return ResponseEntity.status(error.getHttpStatus()).body(result);
    //}

    /**
     * 处理所有未捕获的异常（兜底处理）
     */
    @ExceptionHandler(Exception.class)
    public ResponseEntity<BaseResult<?>> handleUncaughtException(Exception ex) {
        String traceId = TraceUtil.getTraceId();
        // 特殊处理：连接中断异常（如客户端提前关闭连接）
        if (StrUtil.containsIgnoreCase(ExceptionUtils.getRootCauseMessage(ex), "Broken pipe")) {
            log.error("[连接中断异常] traceId: {} \n异常详情: {}", traceId, ex.getMessage());
            // 无需返回响应，避免触发二次异常
            return null;
        }
        // 日志记录：未捕获异常需完整堆栈，便于排查
        log.error("[未捕获异常] traceId: {} \n异常详情:", traceId, ex);
        // 发送告警通知（异步）
        sendErrorAlert(ex, traceId);
        // 使用通用系统错误码，避免暴露敏感信息
        ErrorConstants error = ErrorConstants.SERVER_INTERNAL_ERROR;
        BaseResult<?> result = BaseResult.failure(error.getBizCode(), error.getMsgTemplate());
        return ResponseEntity.status(error.getHttpStatus()).body(result);
    }

    // -------------------------- 工具方法 --------------------------

    /**
     * 构建参数校验错误的响应结果
     */
    private BaseResult<?> buildParamErrorResult(BindingResult bindingResult) {
        if (!bindingResult.hasErrors()) {
            return BaseResult.failure(ErrorConstants.CLIENT_DEFAULT_PARAM_ERROR.getBizCode(),
                    ErrorConstants.CLIENT_DEFAULT_PARAM_ERROR.getMsgTemplate());
        }
        // 获取第一个错误信息（避免返回过多错误给前端）
        FieldError firstError = bindingResult.getFieldErrors().get(0);
        String field = firstError.getField();
        String errorMsg = firstError.getDefaultMessage();
        // 处理默认消息为空或不明确的情况
        if (StrUtil.isBlank(errorMsg) || errorMsg.contains("exception")) {
            errorMsg = ErrorConstants.CLIENT_DEFAULT_PARAM_ERROR.getMsgTemplate();
        } else if (NOT_NULL_MSG.equals(errorMsg) || NOT_EMPTY_MSG.equals(errorMsg)) {
            // 替换通用空值提示为具体字段提示
            errorMsg = StrUtil.format("{} 不能为空", field);
        }
        // 使用参数错误码构建结果
        return BaseResult.failure(ErrorConstants.CLIENT_DEFAULT_PARAM_ERROR.getBizCode(), errorMsg);
    }

    /**
     * 异步发送错误告警（如钉钉、邮件）
     */
    @Async
    protected void sendErrorAlert(Exception ex, String traceId) {
        // 实际项目中可替换为告警发送逻辑（如调用告警服务）
        String errorMsg = ExceptionUtils.getRootCauseMessage(ex);
        // 限制堆栈长度
        String stackTrace = ExceptionUtils.getStackTrace(ex).substring(0, 2000);
        String alertContent = StrUtil.format("[系统异常告警]\ntraceId: {}\n错误消息: {}\n堆栈摘要: {}", traceId, errorMsg,
                stackTrace);

        // 模拟发送：实际应调用告警API
        log.error(alertContent);
    }
}
